#include <QHBoxLayout>
#include <QGraphicsScene>
#include <QPropertyAnimation>
#include <QImage>
#include <QFont>
#include <qmath.h>

#include <dynamicviewsettings.h>

#include "relationshipsview.h"

///////// Class 'FormGraphicsObject' implementation ////////////////////////////////////////////////

FormItem::FormItem(ARSDummyForm * form, QGraphicsItem * parent)
    : QGraphicsObject(parent)
{
    m_form = form;
    m_highlightMode = HighlightNone;
//     QGraphicsDropShadowEffect * shadow = new QGraphicsDropShadowEffect(this);
//     shadow->setBlurRadius(5);
//     shadow->setOffset(0);
//     shadow->setColor(QColor::fromHsv(0, 0, 0));
//     setGraphicsEffect(shadow);
    setAcceptHoverEvents(true);
    setFlag(ItemIsMovable);
    setFlag(ItemSendsGeometryChanges);

    QSize size = boundingRect().size().toSize();
    m_pixmaps.insert(HighlightNone, generatePixmap(QImage(":/skin/form"), size));
    m_pixmaps.insert(HighlightHovered, generatePixmap(QImage(":/skin/form_hovered"), size));
    m_pixmaps.insert(HighlightSource, generatePixmap(QImage(":/skin/form_source"), size));
    m_pixmaps.insert(HighlightDest, generatePixmap(QImage(":/skin/form_dest"), size));
    m_pixmaps.insert(HighlightSourceDest, generatePixmap(QImage(":/skin/form_source_dest"), size));
}

QRectF FormItem::boundingRect() const
{
    QFont font = g_Settings.tableViewsFont();
//    font.setBold(true);
    QFontMetrics fontMetrics(font);
    return QRectF(0, 0, fontMetrics.boundingRect(m_form->name()).width() + 16, FixedHeight);
}

void FormItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QRect itemRect(QPoint(0, 0), m_pixmaps.value(m_highlightMode).size());
    painter->drawPixmap(0, 0, m_pixmaps.value(m_highlightMode));

    QFont font = g_Settings.tableViewsFont();
    painter->setFont(font);
    painter->setPen(QColor::fromHsv(0, 0, 0));
    painter->setRenderHint(QPainter::Antialiasing);
    painter->drawText(itemRect, Qt::AlignCenter, m_form->name());
    painter->setRenderHint(QPainter::Antialiasing, false);
}

ARSDummyForm * FormItem::form() const
{
    return m_form;
}

ArrowItemList & FormItem::inArrows()
{
    return m_inArrows;
}

void FormItem::addInArrow(ArrowItem * item)
{
    m_inArrows.append(item);
}

ArrowItemList & FormItem::outArrows()
{
    return m_outArrows;
}

void FormItem::addOutArrow(ArrowItem * item)
{
    m_outArrows.append(item);
}

QVariant FormItem::itemChange( GraphicsItemChange change, const QVariant & value )
{
    if(change == ItemPositionHasChanged)
        updateArrows();

    return QGraphicsItem::itemChange(change, value);
}

bool FormItem::isRelated(FormItem * item)
{
    foreach(ArrowItem * arrow, m_inArrows)
        if(arrow->from() == item)
            return true;

    foreach(ArrowItem * arrow, m_inArrows)
        if(arrow->to() == item)
            return true;

    return false;
}

void FormItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
    QList<FormItem *> inForms;
    QList<FormItem *> outForms;

    foreach(ArrowItem * item, m_inArrows)
        inForms.append(item->from());

    foreach(ArrowItem * item, m_outArrows)
        outForms.append(item->to());

    foreach(ArrowItem * item, m_inArrows)
    {
        item->setHighlightFactor(ArrowItem::HighlightIn);
        if(outForms.contains(item->from()))
            item->from()->setHighlightMode(HighlightSourceDest);
        else
            item->from()->setHighlightMode(HighlightSource);
    }

    foreach(ArrowItem * item, m_outArrows)
    {
        item->setHighlightFactor(ArrowItem::HighlightOut);
        if(inForms.contains(item->to()))
            item->to()->setHighlightMode(HighlightSourceDest);
        else
            item->to()->setHighlightMode(HighlightDest);
    }

    setHighlightMode(FormItem::HighlightHovered);

    QGraphicsItem::update();
}

void FormItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
    foreach(ArrowItem * item, m_inArrows)
    {
        item->setHighlightFactor(ArrowItem::HighlightNone);
        item->from()->setHighlightMode(HighlightNone);
    }

    foreach(ArrowItem * item, m_outArrows)
    {
        item->setHighlightFactor(ArrowItem::HighlightNone);
        item->to()->setHighlightMode(HighlightNone);
    }

    setHighlightMode(FormItem::HighlightNone);

    QGraphicsItem::update();
}

void FormItem::setHighlightMode(HighlightMode mode)
{
    m_highlightMode = mode;
    update();
}

void FormItem::updateArrows()
{
    foreach(ArrowItem * arrow, m_inArrows)
    {
        arrow->updateGeometry();
    }

    foreach(ArrowItem * arrow, m_outArrows)
    {
        arrow->updateGeometry();
    }
}

QPixmap FormItem::generatePixmap(const QImage & source, QSize size)
{
    QImage leftBorderImage = source.copy(0, 0, 8, source.height());
    QImage rightBorderImage = source.copy(source.width() - 8, 0, 8, source.height());
    QImage middleImage = source.copy(source.width() / 2, 0, 1, source.height());

    QPixmap dest(boundingRect().size().toSize());
    dest.fill(Qt::transparent);
    QPainter painter(&dest);
    painter.drawImage(0, 0, leftBorderImage);
    painter.drawImage(dest.width() - 8, 0, rightBorderImage);
    for(int i = 0; i < dest.width() - 16; i++)
        painter.drawImage(8 + i, 0, middleImage);

    return dest;
}

///////// Class 'ReltionDetailItem' implementation /////////////////////////////////////////////////

ReltionDetailItemView::ReltionDetailItemView(QGraphicsItem * parent)
    : QGraphicsPixmapItem(parent)
{
    m_item = 0;
}

//void ReltionDetailItemView::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
//{
//    painter->drawPixmap(0, 0, m_pixmap);
//}

void ReltionDetailItemView::setItem(ARSExecItem * item)
{
    m_item = item;
    ARSExecItemList list = generateItemList(item);
    QSize size = countSize(list);
    generatePixmap(list, size);
}

void ReltionDetailItemView::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    painter->drawPixmap(0, 0, pixmap());
}

ARSExecItemList ReltionDetailItemView::generateItemList(ARSExecItem * item)
{
    ARSExecItemList list;

    if(item->parent() && item->parent()->type() == ARSExecItem::ItemTypeWorkflowObject)
        list.append(item->parent());

    list.append(item);

    for(int i = 0; i < item->childCount(); i++)
    {
        if(!(item->child(i)->type() & (ARSExecItem::ItemTypeWorkflowBlock | ARSExecItem::ItemTypeAPILine | ARSExecItem::ItemTypeSQLLine)))
            list.append(item->child(i));
    }

    return list;
}

void ReltionDetailItemView::generatePixmap(const ARSExecItemList & list, QSize size)
{
    QPixmap pixmap(size);
    pixmap.fill(Qt::transparent);
    QPainter painter(&pixmap);

    int y = 0;
    for(int i = 0; i < list.count(); i++)
    {
        QRect itemRect = QRect(QPoint(0, y), DynamicViewWidget::itemVisualSize(list.at(i)));
        DynamicViewWidget::drawItem(list.at(i), &painter, itemRect);
        y = itemRect.bottom();
    }

    setPixmap(pixmap);
}

QSize ReltionDetailItemView::countSize(const ARSExecItemList & list) const
{
    QSize size(0, 0);

    foreach(ARSExecItem * item, list)
    {
        QSize itemSize = DynamicViewWidget::itemVisualSize(item);
        size.setHeight(size.height() + itemSize.height());
        if(itemSize.width() > size.width())
            size.setWidth(itemSize.width());
    }

    return size;
}

///////// Class 'ReltionDetailItem' implementation /////////////////////////////////////////////////

ReltionDetailButton::ReltionDetailButton(const QPixmap & pixmap, const QPixmap & hoverPixmap, QGraphicsItem * parent)
    : QGraphicsWidget(parent)
{
    m_pixmap = pixmap;
    m_hoverPixmap = hoverPixmap;
    setAcceptHoverEvents(true);
    setMaximumSize(16, 16);
}

void ReltionDetailButton::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    if(isUnderMouse())
        painter->drawPixmap(0, 0, m_hoverPixmap);
    else
        painter->drawPixmap(0, 0, m_pixmap);
}

void ReltionDetailButton::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    emit clicked();
}

///////// Class 'ReltionDetailLabel' implementation /////////////////////////////////////////////////

ReltionDetailLabel::ReltionDetailLabel(int count, QGraphicsItem * parent)
    : QGraphicsWidget(parent)
{
    m_count = count;
    m_currentIndex = 0;
}

void ReltionDetailLabel::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QString str = QString::number(m_currentIndex) + "/" + QString::number(m_count);

    painter->setPen(QColor::fromHsv(0, 0, 150));
    QFont font("Arial", 9);
    font.setBold(true);
    font.setStyleStrategy(QFont::PreferAntialias);
    painter->setFont(font);
    painter->drawText(rect(), Qt::AlignVCenter | Qt::AlignLeft, str);
}

void ReltionDetailLabel::setCount(int count)
{
    m_count = count;
    update();
}

void ReltionDetailLabel::setCurrentIndex(int index)
{
    m_currentIndex = index;
    update();
}

///////// Class 'ReltionDetailItem' implementation /////////////////////////////////////////////////

ReltionDetailTitle::ReltionDetailTitle(QGraphicsItem * parent)
    : QGraphicsWidget(parent)
{
    m_count = 0;
    m_currentPage = 0;

    QGraphicsLinearLayout * mainLayout = new QGraphicsLinearLayout(Qt::Horizontal, this);
    mainLayout->setContentsMargins(0, 0, 0, 0);
    mainLayout->setSpacing(0);

    ReltionDetailButton * button = new ReltionDetailButton(QPixmap(":/details/close_details"), QPixmap(":/details/close_details_hovered"), this);
    connect(button, SIGNAL(clicked()), this, SIGNAL(closeClicked()));
    mainLayout->addItem(button);

    button = new ReltionDetailButton(QPixmap(":/details/prev_page"), QPixmap(":/details/prev_page_hovered"), this);
    connect(button, SIGNAL(clicked()), this, SLOT(previousPage()));
    mainLayout->addItem(button);

    button = new ReltionDetailButton(QPixmap(":/details/next_page"), QPixmap(":/details/next_page_hovered"), this);
    connect(button, SIGNAL(clicked()), this, SLOT(nextPage()));
    mainLayout->addItem(button);

    m_label = new ReltionDetailLabel(0, this);
    mainLayout->addItem(m_label);

    setLayout(mainLayout);

    setMaximumHeight(FixedHeight);
}

void ReltionDetailTitle::setCount(int count)
{
    m_count = count;
    m_currentPage = 0;
    m_label->setCount(count);
    m_label->setCurrentIndex(1);
}

void ReltionDetailTitle::setCurrentPage(int index)
{
    m_currentPage = index;
    m_label->setCurrentIndex(index + 1);
}

void ReltionDetailTitle::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
//    painter->fillRect(boundingRect(), QColor::fromHsv(0, 0, 240));
}

void ReltionDetailTitle::nextPage()
{
    if(m_currentPage < m_count - 1)
        setCurrentPage(m_currentPage + 1);
    else
        setCurrentPage(0);

    emit pageChanged(m_currentPage);
}

void ReltionDetailTitle::previousPage()
{
    if(m_currentPage > 0)
        setCurrentPage(m_currentPage - 1);
    else
        setCurrentPage(m_count - 1);

    emit pageChanged(m_currentPage);
}

/////////// Class 'ReltionDetailItemView' implementation /////////////////////////////////////////////

//ReltionDetailItemView::ReltionDetailItemView(QGraphicsItem *parent)
//    : QGraphicsWidget(parent)
//{
//    m_item = 0;
//}

//void ReltionDetailItemView::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
//{
//    if(!item)
//        return;
//}

//ARSExecItem * ReltionDetailItemView::item() const
//{
//    return m_item;
//}

//void ReltionDetailItemView::setItem(ARSExecItem * item)
//{
//    m_item = item;
//    update();
//}

///////// Class 'ReltionDetailItem' implementation /////////////////////////////////////////////////

ReltionDetailItem::ReltionDetailItem(QGraphicsItem * parent)
    : QGraphicsObject(parent)
{
    m_highlightFactor = ArrowItem::HighlightNone;
    m_state = Collapsed;
    setAcceptHoverEvents(true);

    m_pixmaps.insert(ArrowItem::HighlightNone, QPixmap(":/skin/details_collapsed"));
    m_pixmaps.insert(ArrowItem::HighlightHovered, QPixmap(":/skin/details_collapsed_hovered"));
    m_pixmaps.insert(ArrowItem::HighlightIn, QPixmap(":/skin/details_collapsed_source"));
    m_pixmaps.insert(ArrowItem::HighlightOut, QPixmap(":/skin/details_collapsed_dest"));

    m_borderPens.insert(ArrowItem::HighlightNone, QPen(QColor::fromHsv(0, 0, 180), 1));
    m_borderPens.insert(ArrowItem::HighlightHovered, QPen(QColor::fromHsv(200, 80, 200), 2));
    m_borderPens.insert(ArrowItem::HighlightIn, QPen(QColor::fromHsv(100, 100, 180), 2));
    m_borderPens.insert(ArrowItem::HighlightOut, QPen(QColor::fromHsv(220, 100, 180), 2));

    m_title = new ReltionDetailTitle(this);
    connect(m_title, SIGNAL(pageChanged(int)), this, SLOT(setPage(int)));
    connect(m_title, SIGNAL(closeClicked()), this, SLOT(collapse()));
    m_title->setPos(margin, margin);

    m_itemView = new ReltionDetailItemView(this);
    m_itemView->setPos(margin, margin + ReltionDetailTitle::FixedHeight);

    m_title->hide();
    m_itemView->hide();
}

void ReltionDetailItem::setHighlightFactor(ArrowItem::HighlightFactor factor)
{
    m_highlightFactor = factor;
    update();
}

void ReltionDetailItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QRectF rect = itemRect();

    if(m_state == Collapsed)
    {
        painter->drawPixmap(0, 0, m_pixmaps.value(m_highlightFactor));
        painter->setPen(Qt::black);
        painter->drawText(rect, Qt::AlignCenter, QString::number(m_itemList.count()));
    }
    else
    {
        painter->setRenderHint(QPainter::Antialiasing);
        painter->setPen(m_borderPens.value(m_highlightFactor));
        QPainterPath path;
        path.addRoundedRect(rect, 4, 4);
        painter->fillPath(path, QColor::fromHsv(0, 0, 250));
        painter->drawPath(path);
        painter->setRenderHint(QPainter::Antialiasing, false);
    }
}

void ReltionDetailItem::setItemList(const ARSExecItemList & list)
{
    m_itemList = list;
    m_title->setCount(list.count());
    setPage(0);
}

void ReltionDetailItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
    if(m_state == Collapsed)
        setExpanded(true);
}

void ReltionDetailItem::setExpanded(bool expanded)
{
    m_state = Animated;

    QPropertyAnimation * animation = new QPropertyAnimation(this);
    animation->setTargetObject(this);
    animation->setPropertyName("animProcess");
    animation->setDuration(200);

    if(expanded)
    {
        animation->setStartValue(0);
        animation->setEndValue(1);
        connect(animation, SIGNAL(finished()), this, SLOT(expandFinished()));
        prepareGeometryChange();
        m_state = Animated;
        setZValue(10);
    }
    else
    {
        m_title->hide();
        m_itemView->hide();
        prepareGeometryChange();
        m_state = Collapsed;
        setZValue(0);
    }

    animation->start(QAbstractAnimation::DeleteWhenStopped);
}

qreal ReltionDetailItem::animProcess() const
{
    return m_animProcess;
}

void ReltionDetailItem::setAnimProcess(qreal process)
{
    prepareGeometryChange();
    m_animProcess = process;
}

QRectF ReltionDetailItem::boundingRect() const
{
    return itemRect().adjusted(-5, -5, 5, 5);
}

void ReltionDetailItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
    setHighlightFactor(ArrowItem::HighlightHovered);
    emit hovered(true);
}

void ReltionDetailItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
    setHighlightFactor(ArrowItem::HighlightNone);
    emit hovered(false);
}

void ReltionDetailItem::expandFinished()
{
    prepareGeometryChange();
    m_title->show();
    m_itemView->show();
    m_state = Expanded;
}

void ReltionDetailItem::collapseFinished()
{
    m_state = Collapsed;
    prepareGeometryChange();
}

void ReltionDetailItem::setPage(int index)
{
    qDebug(Q_FUNC_INFO);
    if(index > m_itemList.count() - 1)
        return;

    prepareGeometryChange();
    m_itemView->setItem(m_itemList.at(index));
}

void ReltionDetailItem::collapse()
{
    setExpanded(false);
}

QRectF ReltionDetailItem::itemRect() const
{
    if(m_state == Collapsed)
        return QRectF(0, 0, 20, 20);

    QRectF expandedRect(QPointF(0, 0), QSizeF(m_itemView->pixmap().width(), ReltionDetailTitle::FixedHeight + m_itemView->pixmap().height()));
    expandedRect.adjust(0, 0, margin * 2, margin * 2);
    if(m_state == Expanded)
        return expandedRect;

    return QRectF(0, 0, expandedRect.width() * m_animProcess, expandedRect.height() * m_animProcess);
}

///////// Class 'ArrowItem' implementation //////////////////////////////////////////////////////////

ArrowItem::ArrowItem(QGraphicsItem * parent, QGraphicsScene * scene)
    : QGraphicsObject(parent)
{
    m_highlightFactor = HighlightNone;

    m_colors.insert(HighlightNone, QPen(QColor::fromHsv(0, 0, 180), 1));
    m_colors.insert(HighlightHovered, QPen(QColor::fromHsv(200, 80, 200), 2));
    m_colors.insert(HighlightIn, QPen(QColor::fromHsv(100, 100, 180), 2));
    m_colors.insert(HighlightOut, QPen(QColor::fromHsv(220, 100, 180), 2));

    m_detailItem = new ReltionDetailItem(0);
    scene->addItem(m_detailItem);
    connect(m_detailItem, SIGNAL(detailsClicked()), this, SLOT(detailsItemClicked()));
    connect(m_detailItem, SIGNAL(hovered(bool)), this, SLOT(hovered(bool)));
}

void ArrowItem::setHighlightFactor(HighlightFactor factor)
{
    m_highlightFactor = factor;
    m_detailItem->setHighlightFactor(factor);
    update();
}

void ArrowItem::setItemList(const ARSExecItemList & list)
{
    m_itemList = list;
    m_detailItem->setItemList(list);
}

const ARSExecItemList & ArrowItem::itemList()const
{
    return m_itemList;
//    return m_detailItem->item
}

void ArrowItem::detailsItemClicked()
{
    emit detailsClicked(this, m_detailItem->boundingRect().translated(m_detailItem->pos()).toRect());
}

void ArrowItem::hovered(bool yes)
{
//    if(yes)
//    {
//        setHighlightFactor(HighlightHovered);
//        m_from->setHighlightMode(FormItem::HighlightDest);
//        m_to->setHighlightMode(FormItem::HighlightSource);
//    }
//    else
//    {
//        setHighlightFactor(HighlightNone);
//        m_from->setHighlightMode(FormItem::HighlightNone);
//        m_to->setHighlightMode(FormItem::HighlightNone);
//    }
}

///////// Class 'ArrowItemLoop' implementation /////////////////////////////////////////////////////

ArrowItemLoop::ArrowItemLoop(FormItem * form, QGraphicsItem * parent, QGraphicsScene * scene)
    : ArrowItem(parent, scene)
{
    m_formItem = form;
    m_rect = QRectF(0, 0, FormItem::FixedHeight, FormItem::FixedHeight);
}

void ArrowItemLoop::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QPolygonF polygon;
    polygon.append(QPointF(m_rect.width() / 2, m_rect.bottom()));
    polygon.append(QPointF(m_rect.width() / 2 + 8, m_rect.bottom() - 4));
    polygon.append(QPointF(m_rect.width() / 2 + 8, m_rect.bottom() + 4));
    QPainterPath path;
    path.addPolygon(polygon);

    QPen pen = m_colors.value(m_highlightFactor);
    painter->setPen(pen);
    painter->setRenderHint(QPainter::Antialiasing);
    painter->drawArc(m_rect, -90 * 16, 270 * 16);
    painter->fillPath(path, pen.color());
    painter->setRenderHint(QPainter::Antialiasing, false);
}

FormItem * ArrowItemLoop::from() const
{
    return m_formItem;
}

FormItem * ArrowItemLoop::to() const
{
    return m_formItem;
}

void ArrowItemLoop::updateGeometry()
{
    setPos(m_formItem->pos().x() + m_formItem->boundingRect().width() - FormItem::FixedHeight / 2,
           m_formItem->pos().y() - FormItem::FixedHeight / 2);

    m_detailItem->setPos(pos().x() + FormItem::FixedHeight, pos().y());
}

QRectF ArrowItemLoop::boundingRect() const
{
    return m_rect.adjusted(-10, -10, 10, 10);
}

///////// Class 'ArrowItemLine' implementation /////////////////////////////////////////////////////

ArrowItemLine::ArrowItemLine(FormItem * from, FormItem * to, QGraphicsItem * parent, QGraphicsScene * scene)
    : ArrowItem(parent, scene)
{
    m_from = from;
    m_to = to;
}

void ArrowItemLine::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
    QPolygonF polygon;
    polygon.append(QPointF(0, 0));
    polygon.append(QPointF(-8, -4));
    polygon.append(QPointF(-8, 4));
    QPainterPath path;
    path.addPolygon(polygon);
    QPen pen = m_colors.value(m_highlightFactor);

    painter->save();
    painter->setRenderHint(QPainter::Antialiasing);
    painter->setPen(pen);
    painter->drawLine(m_line);
    painter->translate(m_line.p2());
    painter->rotate(360 - m_line.angle());

    painter->fillPath(path, pen.color());
    painter->restore();
}
FormItem * ArrowItemLine::from() const
{
    return m_from;
}

FormItem * ArrowItemLine::to() const
{
    return m_to;
}

void ArrowItemLine::updateGeometry()
{
    QRectF srcRect = m_from->boundingRect().translated(m_from->pos());
    QRectF destRect = m_to->boundingRect().translated(m_to->pos());
    QLineF line(srcRect.center(), destRect.center());
    QLineF n = line.normalVector();
    n.setLength(4);
    line.setPoints(QPoint(line.x1() + n.dx(), line.y1() + n.dy()), QPoint(line.x2() + n.dx(), line.y2() + n.dy()));

    if(!srcRect.intersects(destRect))
        line.setPoints(intersectPoint(line, srcRect), intersectPoint(line, destRect));

    n.setLength(14);
    prepareGeometryChange();
    setPos(qMin(line.x1(), line.x2()), qMin(line.y1(), line.y2()));
    m_detailItem->setPos((line.x1() + line.x2()) / 2 + n.dx() - 8, (line.y1() + line.y2()) / 2 + n.dy() - 8);

    m_line = line.translated(-pos().x(), -pos().y());
    update();
}

QRectF ArrowItemLine::boundingRect() const
{
    return QRectF(0, 0, qAbs(m_line.dx()), qAbs(m_line.dy())).adjusted(-10, -10, 10, 10);
}

QPointF ArrowItemLine::intersectPoint(QLineF line, QRectF rect)
{
    QPointF result;
    QLineF::IntersectType intersectType;

    intersectType = line.intersect(QLineF(rect.topLeft(), rect.topRight()), &result);
    if(intersectType == QLineF::BoundedIntersection)
        return result;

    intersectType = line.intersect(QLineF(rect.topRight(), rect.bottomRight()), &result);
    if(intersectType == QLineF::BoundedIntersection)
        return result;


    intersectType = line.intersect(QLineF(rect.bottomLeft(), rect.bottomRight()), &result);
    if(intersectType == QLineF::BoundedIntersection)
        return result;


    intersectType = line.intersect(QLineF(rect.topLeft(), rect.bottomLeft()), &result);
    if(intersectType == QLineF::BoundedIntersection)
        return result;

    return QPointF();
}

///////// Class 'RelationshipsView' implementation /////////////////////////////////////////////////

RelationshipsView::RelationshipsView(QWidget * parent)
    : QGraphicsView(parent)
{
    m_model = 0;
    QGraphicsScene * scene = new QGraphicsScene(0, 0, 1000, 1000);
    scene->setBackgroundBrush(Qt::white);
    setScene(scene);
    setMouseTracking(true);
    m_timer = new QTimer(this);
    m_timer->setInterval(50);
    connect(m_timer, SIGNAL(timeout()), this, SLOT(animate()));
}

void RelationshipsView::setModel(DynamicModel * model)
{
    m_model = model;

    if(!model)
        return;

    ARSDummyObjectList objectList = model->index()->objects(ARSObject::Form);
    int counter = 0;
    int columnsCount = qSqrt(objectList.count());
    foreach(ARSDummyObject * object, objectList)
    {
        ARSDummyForm * form = qobject_cast<ARSDummyForm *>(object);
        FormItem * formItem = new FormItem(form);
        scene()->addItem(formItem);
        formItem->setPos((counter % columnsCount) * 200 + 100 + ((counter / columnsCount) % 2) * 100, (counter / columnsCount) * 40 + 100);
        m_formList.append(formItem);
        counter++;
    }

    findRelations();
//    m_timer->start();
}

void RelationshipsView::animate()
{
    QHash<FormItem *, QLine> m_forceHash;

    foreach(FormItem * item, m_formList)
    {
        QLine itemForce = force(item);
        m_forceHash.insert(item, itemForce);
    }

    foreach(FormItem * item, m_formList)
    {
        QLine itemForce = m_forceHash.value(item);
        item->moveBy(itemForce.x2() - itemForce.x1(), itemForce.y2() - itemForce.y1());
    }
}

void RelationshipsView::findRelations()
{
    foreach(FormItem * targetFormItem, m_formList)
    {
        ARSExecItemList relatedItemList = m_model->index()->relatedItems(targetFormItem->form());
        QHash<ARSDummyForm *, ARSExecItem *> sourceList;
        foreach(ARSExecItem * item, relatedItemList)
        {
            ARSExecItem * blockItem = item->findParent(ARSExecItem::ItemTypeWorkflowBlock);
            if(blockItem)
            {
                ARSExecWorkflowBlock * block = qobject_cast<ARSExecWorkflowBlock *>(blockItem);
                if(block)
                {
                    ARSDummyForm * sourceForm = block->form();
                    if(sourceForm)
                        sourceList.insertMulti(sourceForm, item->parent());
                }
            }
        }

        foreach(ARSDummyForm * sourceForm, sourceList.uniqueKeys())
        {
            FormItem * sourceFormItem = formItem(sourceForm);
            if(sourceFormItem)
            {
                ArrowItem * arrow;

                if(sourceFormItem == targetFormItem)
                    arrow = new ArrowItemLoop(targetFormItem, 0, scene());
                else
                    arrow = new ArrowItemLine(sourceFormItem, targetFormItem, 0, scene());

                arrow->setItemList(sourceList.values(sourceForm));
                scene()->addItem(arrow);
                arrow->updateGeometry();
                sourceFormItem->addOutArrow(arrow);
                targetFormItem->addInArrow(arrow);
                m_arrowList.append(arrow);
            }
        }
    }
}

FormItem * RelationshipsView::formItem(ARSDummyForm * form)
{
    foreach(FormItem * item, m_formList)
        if(item->form() == form)
            return item;

    return 0;
}

QLine RelationshipsView::force(FormItem * item)
{
    QLineF result(0, 0, 0, 0);
    float k = 0.1;
    QPointF itemCenter = item->boundingRect().translated(item->pos()).center();
    qreal xforce;
    qreal yforce;
    foreach(FormItem * other, m_formList)
    {
        if(item == other)
            continue;

        QLineF currentForce(itemCenter, other->boundingRect().translated(other->pos()).center());
        currentForce.translate(-currentForce.x1(), -currentForce.y1());
        xforce = currentForce.dx();
        qreal currentForceAbs = currentForce.dx() * currentForce.dx() + currentForce.dy() * currentForce.dy();
//        if(item->isRelated(other))
//        {
//            k = 0.01;
//            currentForce.setP2(QPointF(currentForceAbs * currentForce.x2() * k, currentForceAbs * currentForce.y2() * k));
//        }
//        else
        {
            k = -200;
            currentForce.setP2(QPointF(k / (currentForceAbs * currentForce.x2()), k / (currentForceAbs * currentForce.y2())));
        }

        xforce += currentForce.dx() / currentForceAbs;
        yforce += currentForce.dx() / currentForceAbs;
    }

    return result.toLine();
}

///////// Class 'RelationshipsViewWidget' implementation ///////////////////////////////////////////

RelationshipsViewWidget::RelationshipsViewWidget(QWidget * parent)
    : DynamicViewWidget(parent)
{
    QHBoxLayout * mainLayout = new QHBoxLayout(this);
    mainLayout->setContentsMargins(0, 0, 0, 0);

    m_view = new RelationshipsView(this);
    mainLayout->addWidget(m_view);

    setLayout(mainLayout);
}

QString RelationshipsViewWidget::pluginName() const
{
    return "relationshipsview";
}

void RelationshipsViewWidget::connectToolBarItems(const ToolBarItemList & items)
{

}

void RelationshipsViewWidget::disconnectToolBarItems(const ToolBarItemList & items)
{

}

void RelationshipsViewWidget::adjustToolbar(const ToolBarItemList & itemsList) const
{

}

void RelationshipsViewWidget::setModel(DynamicModel * model)
{
    m_view->setModel(model);
}

///////// Class 'RelationshipsViewPlugin' implementation ///////////////////////////////////////////

RelationshipsViewPlugin::RelationshipsViewPlugin(QObject * parent)
    : QObject(parent)
{
}

QString RelationshipsViewPlugin::name()const
{
    return "relationshipsview";
}

QString RelationshipsViewPlugin::description()const
{
    return tr("This plugin provides relationship diagram representation of ARS log files");
}

bool RelationshipsViewPlugin::loadSettings()
{
    return true;
}

QString RelationshipsViewPlugin::viewName()const
{
    return tr("Relationships");
}

ViewWidget::ViewType RelationshipsViewPlugin::viewType() const
{
    return ViewWidget::DynamicModelView;
}

QIcon RelationshipsViewPlugin::icon(bool active)const
{
    QPixmap pixmap(20, 20);
    pixmap.fill(QColor(255, 150, 255));
    return QIcon(pixmap);
}

ViewWidget * RelationshipsViewPlugin::createView(QWidget * parent)const
{
    return new RelationshipsViewWidget(parent);
}

ToolBarItemList RelationshipsViewPlugin::toolBarItems(QObject * parent)const
{
    return ToolBarItemList();
}

SettingsWidget * RelationshipsViewPlugin::settingsWidget(QWidget * parent)const
{
    return 0;
}

void RelationshipsViewPlugin::appEvent(AppEvent * event)
{
}

////////////////////////////////////////////////////////////////////////////////////////////////////

Q_EXPORT_PLUGIN2(relationshipsview, RelationshipsViewPlugin)

////////////////////////////////////////////////////////////////////////////////////////////////////
